import datetime
import traceback

from django.db import transaction

from app.health_manage.models import ServerHealth, ServerLifeScore, FaultFrequencyScore, FaultScore, HealthLevel
from app.health_manage.other_models import InspectionInfoList, ResourcePhysicalMachine
from app.health_manage.utils import is_score_range, query_server, dict_handle
from app.vecmdb import get_ci
from common.libs.common_data_opt import del_dict_key, diff_days
from dateutil.relativedelta import relativedelta
from concurrent.futures import ThreadPoolExecutor


def life_core_calculate(warranty_since):
    """寿命分数计算"""
    if not warranty_since:
        return 0, None, 1
    days = diff_days(warranty_since, datetime.datetime.now().today().date())
    if days < 0:
        return 0, '稳定期', 1

    info = ServerLifeScore.objects.all()

    score = 0
    life_stage = None
    weight = 1
    for item in info:
        if is_score_range(item.life, int(days / 30)):
            score = item.score
            life_stage = item.lifeDate
            weight = item.weight
    if not life_stage:
        print(warranty_since, days)
    return score, life_stage, weight


def fault_frequency_score_calculate(sn):
    """故障间隔分"""
    now_time = datetime.datetime.now()

    # 获取前15天的时间
    # end_time = now_time - datetime.timedelta(days=15)
    six_months_ago = now_time - relativedelta(months=6)

    info = InspectionInfoList.objects.using('server-db').filter(is_deleted=0, sn=sn, first_find_time__range=(six_months_ago, now_time)).values('first_find_time', 'fault_type').order_by('-first_find_time')
    if len(info) < 3:
        return 0, None, None
    else:
        days = diff_days(info[2]['first_find_time'], info[0]['first_find_time'])
        # interval = round(days / 3, 2)

        score = 0
        frequency_name = None
        info2 = FaultFrequencyScore.objects.all()
        for item in info2:
            if is_score_range(item.days, days):
                score = item.score
                frequency_name = item.frequencyName

        return score, days, frequency_name


def fault_score_calculate(sn, weight):
    """故障分计算"""
    info = InspectionInfoList.objects.using('server-db').filter(is_deleted=0, sn=sn, status__in=[0, 1]).values('first_find_time', 'fault_type').order_by('-first_find_time')
    if not info:
        return 0

    score = 0
    for item in info:
        tmp = FaultScore.objects.extra(where=["FIND_IN_SET('%s', faultType)" % (item['fault_type'])]).values().first()
        if tmp:
            score = score + tmp['score']
    return score * weight


def health_status(score):
    """获取健康度状态"""
    remain_score = 100 - score
    if remain_score <= 0:
        return '病危', round(remain_score, 2)
    else:
        health = ''
        info = HealthLevel.objects.all()
        for item in info:
            if is_score_range(item.score, remain_score):
                health = item.healthLevel
    return health, round(remain_score, 2)


# def health_basic_data_sync():
#     """同步cmdb基础数据"""
#     # category = "通算;智算;存储"
#     # payload = {
#     #     "q": f"_type:PhysicalMachineResource,bigCategory:({category})",
#     #     "count": 100000,
#     #     "page": 1,
#     # }
#     # res = get_ci(payload)
#     # if 'message' in res:
#     #     return False, res.get('message')
#     # info = res.get('result')
#     # if not info:
#     #     return False, '数据为空'
# 
#     info = ResourcePhysicalMachine.objects.using('cmdb_online').all()
#     try:
#         tmp = []
#         for item in info:
#             new_info = {
#                 "systemSN": item.get("systemSN", None),
#                 "manageIp": item.get("manageIp", None),
#                 "ipmi": item.get("ipmi", None),
#                 "dataCenter": item.get("dataCenter", None),
#                 "rackBuilding": item.get("rackBuilding", None),
#                 "rackFloor": item.get("rackFloor", None),
#                 "rackModule": item.get("rackModule", None),
#                 "rackCode": item.get("rackCode", None),
#                 "uLocationStart": item.get("uLocationStart", None),
#                 "uLocationEnd": item.get("uLocationEnd", None),
#                 "userAccount": item.get("userAccount", None),
#                 "userProductLine": item.get("userProductLine", None),
#                 "userDivision": item.get("userDivision", None),
#                 "model": item.get("model", None),
#                 "manu": item.get("manu", None),
#                 "warrantyEnd": item.get("warrantyEnd", None),
#                 "warrantySince": item.get("warrantySince", None),
#                 "arrivalDate": item.get("arrivalDate", None),
#                 "isDeleted": 0
#             }
#             sn = new_info.get('systemSN')
# 
#             life_score, life_stage, weight = life_core_calculate(item.get("warrantySince", None))
#             fault_frequency_score, interval, frequency_name = fault_frequency_score_calculate(sn)
#             fault_score = fault_score_calculate(sn, weight)
# 
#             total_score = life_score + fault_frequency_score + fault_score
#             health, health_score = health_status(total_score)
#             # print(life_score, life_stage, fault_frequency_score, interval, fault_score)
#             # print(health, health_score)
# 
#             add_info = {
#                 "health": health,
#                 "healthScore": health_score,
#                 "calculateTime": datetime.datetime.now(),
#                 "faultFrequency": frequency_name,
#                 "lifeTime": life_stage,
#                 "lockStatus": "未锁定"
#             }
# 
#             all_info = {**new_info, **add_info}
#             if ServerHealth.objects.filter(systemSN=sn).exists():
#                 del new_info['systemSN']
#                 server = ServerHealth.objects.filter(systemSN=sn).first()
#                 if server.lockStatus == '锁定':
#                     del all_info['health']
#                     del all_info['lockStatus']
#                 ServerHealth.objects.filter(systemSN=sn).update(**all_info)
#             else:
#                 tmp.append(ServerHealth(**all_info))
#         ServerHealth.objects.bulk_create(tmp)
#     except Exception as e:
#         print(traceback.print_exc())
#         return False, str(e)
#     return True, 'success'


def process_health_item(item):
    """处理单个资源项，返回（sn, ServerHealth对象, 是否更新, 是否锁定）"""
    sn = item.get("systemSN")
    new_info = {
        "systemSN": sn,
        "manageIp": item.get("manageIp"),
        "ipmi": item.get("ipmi"),
        "dataCenter": item.get("dataCenter"),
        "rackBuilding": item.get("rackBuilding"),
        "rackFloor": item.get("rackFloor"),
        "rackModule": item.get("rackModule"),
        "rackCode": item.get("rackCode"),
        "uLocationStart": item.get("uLocationStart"),
        "uLocationEnd": item.get("uLocationEnd"),
        "userAccount": item.get("userAccount"),
        "userProductLine": item.get("userProductLine"),
        "userDivision": item.get("userDivision"),
        "model": item.get("model"),
        "manu": item.get("manu"),
        "warrantyEnd": item.get("warrantyEnd"),
        "warrantySince": item.get("warrantySince"),
        "arrivalDate": item.get("arrivalDate"),
        "isDeleted": 0
    }

    # 评分计算
    life_score, life_stage, weight = life_core_calculate(item.get("warrantySince"))
    fault_frequency_score, interval, frequency_name = fault_frequency_score_calculate(sn)
    fault_score = fault_score_calculate(sn, weight)
    total_score = life_score + fault_frequency_score + fault_score
    health, health_score = health_status(total_score)

    add_info = {
        "health": health,
        "healthScore": health_score,
        "calculateTime": datetime.datetime.now(),
        "faultFrequency": frequency_name,
        "lifeTime": life_stage,
        "lockStatus": "未锁定"
    }

    all_info = {**new_info, **add_info}
    return sn, all_info


def health_basic_data_sync():
    """同步 cmdb 基础数据（多线程优化版）"""
    try:
        # 拉数据
        info_qs = ResourcePhysicalMachine.objects.using('cmdb_online').filter(bigCategory__in=['智算', '通算', '存储'], is_deleted=0).all()
        info_list = list(info_qs.values())  # 转为 dict，避免线程中 ORM 操作

        # 多线程并发计算评分 + 拼接数据
        with ThreadPoolExecutor(max_workers=12) as executor:
            results = list(executor.map(process_health_item, info_list))

        # 查询现有 SN 映射
        exist_sn_map = {
            obj.systemSN: obj
            for obj in ServerHealth.objects.filter(systemSN__in=[r[0] for r in results])
        }

        insert_objs = []
        update_objs = []

        for sn, data in results:
            if sn in exist_sn_map:
                server_obj = exist_sn_map[sn]
                # 判断是否锁定，不更新 health 字段
                if server_obj.lockStatus == '锁定':
                    data.pop('health', None)
                    data.pop('lockStatus', None)
                for key, value in data.items():
                    setattr(server_obj, key, value)
                update_objs.append(server_obj)
            else:
                insert_objs.append(ServerHealth(**data))

        with transaction.atomic():
            if insert_objs:
                ServerHealth.objects.bulk_create(insert_objs, batch_size=500)
            if update_objs:
                ServerHealth.objects.bulk_update(update_objs, fields=list(update_objs[0].__dict__.keys())[2:], batch_size=500)

        return True, 'success'
    except Exception as e:
        print(traceback.format_exc())
        return False, str(e)


def server_reverse_query():
    """反向查询 - 确认已下架资产，批量置为 isDeleted=1（优化版）"""

    # 获取所有在架系统SN（from cmdb_online）
    online_sn_set = set(
        ResourcePhysicalMachine.objects.using('cmdb_online').filter(bigCategory__in=['智算', '通算', '存储'], is_deleted=0).values_list('systemSN', flat=True)
    )

    # 获取所有 ServerHealth 记录
    all_servers = ServerHealth.objects.all()

    # 需要更新 isDeleted 的记录列表
    to_update = [server for server in all_servers if server.systemSN not in online_sn_set and server.isDeleted != 1]

    # 批量设置 isDeleted = 1
    for server in to_update:
        server.isDeleted = 1

    # 批量更新
    if to_update:
        with transaction.atomic():
            ServerHealth.objects.bulk_update(to_update, fields=['isDeleted'], batch_size=500)

    return True


# def server_reverse_query():
#     """反向查询，确认资产是否下架移除"""
#     info = ServerHealth.objects.all()
#
#     for item in info:
#         if not ResourcePhysicalMachine.objects.using('cmdb_online').filter(systemSN=item.systemSN).exists():
#             ServerHealth.objects.filter(id=item.id).update(isDeleted=1)
#             # res, msg = query_server(item.systemSN)
#             # if not res and msg == 'empty':
#             #     print(res, msg)
#             #     ServerHealth.objects.filter(id=item.id).update(isDeleted=1)
#     return True


def dashboard_selections():
    """获取总览筛选级联组件选项"""
    rooms = dict_handle(ServerHealth, "dataCenter", "default", filter_data=None)
    manus = dict_handle(ServerHealth, "manu", "default", filter_data=None)
    departs = dict_handle(ServerHealth, "userProductLine", "default", filter_data=None)
    years = [{"label": "稳定期", "value": "稳定期"},
             {"label": "风险期", "value": "风险期"},
             {"label": "退役期", "value": "退役期"},
             {"label": "报废期", "value": "报废期"}]

    data = [
        {
            "label": "全局",
            "value": "全局",
            "children": []
        },
        {
            "label": "机房",
            "value": "机房",
            "children": rooms
        },
        {
            "label": "年限",
            "value": "年限",
            "children": years
        },
        {
            "label": "业务线",
            "value": "业务线",
            "children": departs
        },
        {
            "label": "厂商",
            "value": "厂商",
            "children": manus
        }
    ]
    return data


def dashboard_pie_data(params):
    """饼图数据"""
    value1 = params.get('value1', None)
    value2 = params.get('value2', None)

    if value1 == '全局' or not value1:
        count1 = ServerHealth.objects.filter(isDeleted=0, health='健康').count()
        count2 = ServerHealth.objects.filter(isDeleted=0, health='亚健康').count()
        count3 = ServerHealth.objects.filter(isDeleted=0, health='病危').count()
    elif value1 == '年限':
        count1 = ServerHealth.objects.filter(isDeleted=0, health='健康', lifeTime=value2).count()
        count2 = ServerHealth.objects.filter(isDeleted=0, health='亚健康', lifeTime=value2).count()
        count3 = ServerHealth.objects.filter(isDeleted=0, health='病危', lifeTime=value2).count()
    elif value1 == '机房':
        count1 = ServerHealth.objects.filter(isDeleted=0, health='健康', dataCenter=value2).count()
        count2 = ServerHealth.objects.filter(isDeleted=0, health='亚健康', dataCenter=value2).count()
        count3 = ServerHealth.objects.filter(isDeleted=0, health='病危', dataCenter=value2).count()
    elif value1 == '业务线':
        count1 = ServerHealth.objects.filter(isDeleted=0, health='健康', userProductLine=value2).count()
        count2 = ServerHealth.objects.filter(isDeleted=0, health='亚健康', userProductLine=value2).count()
        count3 = ServerHealth.objects.filter(isDeleted=0, health='病危', userProductLine=value2).count()
    elif value1 == '厂商':
        count1 = ServerHealth.objects.filter(isDeleted=0, health='健康', manu=value2).count()
        count2 = ServerHealth.objects.filter(isDeleted=0, health='亚健康', manu=value2).count()
        count3 = ServerHealth.objects.filter(isDeleted=0, health='病危', manu=value2).count()
    else:
        count1 = 0
        count2 = 0
        count3 = 0
    data = [{
        "name": "健康",
        "value": count1
      }, {
        "name": '亚健康',
        "value": count2
      }, {
        "name": "病危",
        "value": count3
      }]
    return True, data



